#include <cstring>
#include <fstream>
#include <regex>
#include <iostream>

#include "WebServer.h"
#include "Defines.h"

WebServer::WebServer() {
    this->sockfd = socket(AF_INET, SOCK_STREAM, 0);
    this->socketAddr.sin_family = AF_INET;
    this->socketAddr.sin_port = htons(PORT_NUM);
    this->socketAddr.sin_addr.s_addr = htonl(INADDR_ANY);  // 来源IP为0.0.0.0

    bind(this->sockfd, (sockaddr *)&this->socketAddr, sizeof(this->socketAddr));
    listen(this->sockfd, MAX_CLIENTS);
}

WebServer::~WebServer() {
    close(this->sockfd);
    std::cout << "WebServer Closed.\n";
}

void WebServer::generateResponsePacket(
    const std::string &data, 
    const int code, 
    std::string &response
) {
    response.append("HTTP/1.1 ");
    const std::string statusDescription = (code == 200) ? "200 OK\n" : "404 Not Found\n";
    response.append(statusDescription);

    std::string contentLength = std::to_string(data.size());
    response.append("Content-Length: " + contentLength + "\n\n");
    response.append(data);
}

void WebServer::generateResponsePacket(
    const std::string &data, 
    const int code, 
    const std::string &contentType,
    std::string &response
) {
    response.append("HTTP/1.1 ");
    const std::string statusDescription = (code == 200) ? "200 OK\n" : "404 Not Found\n";
    response.append(statusDescription);

    response.append("Content-Type: " + contentType + "\n");

    std::string contentLength = std::to_string(data.size());
    response.append("Content-Length: " + contentLength + "\n\n");
    response.append(data);
}

void WebServer::handlePost(const std::string &path, const std::string &args, std::string &response) {
    int argsLength = args.size();
    int start = 0, end = 0;

    std::string username, password;
    for (int i = 0; i < argsLength; i++) {
        if (args.at(i) == '=') {
            start = i + 1;
        } else if (args.at(i) == '&') {
            end = i;
            username = args.substr(start, end - start);
            password = args.substr(end + 6);
            break;
        }
    }

    std::cout << username << " " << password << "\n";
    bool authorized = username == "admin" && password == "123456";

    const std::string successPrompt = "<html><body>OK, you win!</body></html>";
    const std::string failPrompt = "<html><body>Failed to authorize...</body></html>";
    const std::string contentType = "text/html";

    std::string prompt = authorized ? successPrompt : failPrompt;

    WebServer::generateResponsePacket(prompt, 200, contentType, response);
}

void WebServer::handleGet(const std::string &path, std::string &response) {
    std::regex JPG_FILE_PATTERN("\\.(jpg|jpeg|JPG|JPEG)$");
    std::regex TXT_FILE_PATTERN("\\.(txt|TXT)$");
    std::ifstream fileStream;

    std::string contentType;
    if (std::regex_search(path, JPG_FILE_PATTERN)) {
        fileStream.open(path, std::ios::in | std::ios::binary);
        contentType = "image/jpeg";
    } else {
        fileStream.open(path, std::ios::in);
        if (std::regex_search(path, TXT_FILE_PATTERN)) {
            contentType = "text/plain";
        } else {
            contentType = "text/html";
        }
    }

    std::string fileData;
    // 读取所有内容
    try {
        fileData.assign(std::istreambuf_iterator<char>(fileStream), std::istreambuf_iterator<char>());
    } catch (std::ifstream::failure exception) {
        std::cout << "exception on ifstream: " << exception.what() << "\n";
    } 
    fileStream.close();

    bool is404 = path == "../root/html/404.html";
    WebServer::generateResponsePacket(fileData, is404 ? 404 : 200, contentType, response);
}

void WebServer::router(const std::string &requestPath, std::string &realPath) {
    realPath = ROOT_PATH;
    if (requestPath == "/") {
        realPath += "/html/test.html";
    } else if (requestPath == "/noimg.html") {
        realPath += "/html/noimg.html";
    } else if (requestPath == "/test.txt") {
        realPath += "/test.txt";
    } else if (requestPath == "/favicon.ico") {
        realPath += "/img/favicon.ico";
    } else if (requestPath == "/img/cat.jpg") {
        realPath += "/img/cat.jpg";
    } else if (requestPath == "/dopost") {
        realPath = "/dopost";
    } else {
        realPath += "/html/404.html";
    } 
}

void WebServer::handleRequest(const std::string &request, std::string &response) {
    std::regex whitespace("\\s+");  // 匹配 >=1 个空格 (转义符用\\)
    std::vector<std::string> words(std::sregex_token_iterator(
        request.begin(), request.end(), whitespace, -1
    ), std::sregex_token_iterator());  // 这就是C++中的line.split()

    // // DEBUG purpose
    // for (auto word : words) {
    //     std::cout << word << " ";
    // } std::cout << "\n";

    std::string method = words[0];
    std::string requestPath = words[1];
    std::string path;
    WebServer::router(requestPath, path);


    if (method == "GET") {
        WebServer::handleGet(path, response);
    } else if (method == "POST") {
        try {
            const std::string args = request.substr(request.find("\r\n\r\n", 0) + 4);
            std::cout << "POST args: " << args << "\n";
            WebServer::handlePost(path, args, response);
        } catch (std::logic_error exception) {
            std::cout << "exception on POST: " << exception.what() << "\n";
        } catch (...) {
            std::cout << "unknown exception on POST!!\n";
        }
    }
}

void WebServer::threadRoutine(int sfd) {
    char buffer[MAX_BUFFER_SIZE] = {0};

    int msgLength = recv(sfd, buffer, MAX_BUFFER_SIZE, 0);
    std::string request(buffer, buffer + msgLength);

    std::string response;
    WebServer::handleRequest(request, response);

    send(sfd, response.c_str(), response.size(), 0);
    close(sfd);
}

void *WebServer::threadStartRoutine(void *sfd) {
    WebServer::threadRoutine(*(int *)sfd);
    return nullptr;
}

void WebServer::run() {
    std::cout << "WebServer Listening\n";
    while (true) {
        sockaddr_in clientSocketAddr;
        unsigned int clientSocketAddrLen = sizeof(clientSocketAddr);

        int connFd = accept(
            this->sockfd, 
            (sockaddr *)&clientSocketAddr,
            (socklen_t *)&clientSocketAddrLen
        );

        std::cout << inet_ntoa(clientSocketAddr.sin_addr)
                  << ":"
                  << ntohs(clientSocketAddr.sin_port)
                  << " connected! \n";

        pthread_t connThread;  // new thread create
        pthread_create(
            &connThread, 
            nullptr, 
            this->threadStartRoutine, 
            &connFd  // thread fd
        );
    }
}

int main() {
    WebServer *webServer = new WebServer();
    webServer->run();
    delete webServer;
    return 0;
}